/**
 * Created By xiyuan
 * Author server@xiyuan.name (惜缘叛逆)
 * DateTime 2019/3/10 9:41 PM
 * Describe javascript功能描述
 * MIT License http://www.opensource.org/licenses/mit-license.php
 */

// 系统包
const os = require('os');
// 文件处理包
const fs = require('fs');
// 路径包
const path = require('path');
// 逐行读取
const readline = require('readline');
// 转换流
const {Transform} = require('stream');
// 请求工具包
const request = require('request');
// 压缩包提取工具
const decompress = require('decompress');
// 工具集
const {dirExists} = require('../lib/utils');

// 安装尝试次数
const maxAttempts = 1;

// 资源地址
const sourceUrl = getSourceUrl();

// 缓存临时地址 (用来确保资源是否下载完毕)
const cacheTmpUrl = getcacheTmpUrl();

// 缓存地址
const cacheUrl = cacheTmpUrl + '.cache';


// 指令目录,确保目录存在
const binDir = dirExists(path.join(__dirname, '/../bin'));

// 检查是否存在缓存
if (hasCache()) {
	console.log('FRP - 缓存下载' + cacheUrl);
	extract(retry);
} else {
	download(retry);
}


// 获取资源地址
function getSourceUrl() {
	// 计算机架构
	const arch = process.env.FRP_ARCH || (os.platform() + os.arch());
	const sourceUrl = process.env.FRP_URL || 'http://xiyuan.gitee.io' || 'https://github.com';
	const sourcePath = process.env.FRP_PATH || '/downstore/frp_dir/frp_0.24.1_' || '/fatedier/FRP/releases/download/v0.24.1/FRP_0.24.1_';
	const sourceUrlFiles = {
		darwinx64: sourceUrl + sourcePath + 'darwin_amd64.tar.gz',
		linuxarm: sourceUrl + sourcePath + 'linux_arm.tar.gz',
		linuxarm64: sourceUrl + sourcePath + 'linux_arm64.tar.gz',
		linuxia32: sourceUrl + sourcePath + 'linux_386.tar.gz',
		linuxx64: sourceUrl + sourcePath + 'linux_amd64.tar.gz',
		win32ia32: sourceUrl + sourcePath + 'windows_386.zip',
		win32x64: sourceUrl + sourcePath + 'windows_amd64.zip',
		freebsdia32: sourceUrl + sourcePath + 'freebsd_386.tar.gz',
		freebsdx64: sourceUrl + sourcePath + 'freebsd_amd64.tar.gz'
	};
	const url = sourceUrlFiles[arch];
	if (!url) {
		console.error('FRP - 目前不支持' + arch + '平台架构,如有需要请联系 mail:server@xiyuan.name');
		process.exit(1);
	}
	return url;
}

// 获取缓存地址
function getcacheTmpUrl() {
	let dir;
	try {
		dir = path.join(os.homedir(), '.FRP');
		// 检查用户根目录下是否存在缓存目录
		if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
			// 同步创建缓存目录
			fs.mkdirSync(dir);
		}
	} catch (err) {
		// 如果失败采用当前目录中的bin目录
		dir = binDir;
	}
	
	// 生成文件名称
	const name = Buffer.from(sourceUrl).toString('base64');
	
	return path.join(dir, name + '');
}

// 检查是否有缓存
function hasCache() {
	// 检查是否忽略缓存
	return process.env.FRP_IGNORE_CACHE !== 'true'
		// 检查缓存文件是否存在
		&& fs.existsSync(cacheUrl)
		// 检查缓存资源是否有效
		&& fs.statSync(cacheUrl).size;
}

// 资源下载
function download(cb) {
	console.log('FRP - 下载二进制资源 ' + sourceUrl);
	// 检查是否存在缓存
	if (hasCache()) {
		return extract(cb);
	}
	
	// 进行下载
	const downloadStream = request
		.get(sourceUrl)
		// 报文体
		.on('response', res => {
			// 检查资源状态
			if (!/2\d\d/.test(res.statusCode)) {
				// 暂停资源下载
				res.pause();
				// 触发错误提示
				return downloadStream.emit('error', new Error('wrong status code: ' + res.statusCode));
			}
			// 资源长度
			const total = res.headers['content-length'];
			const progress = progressStream('FRP - 下载进度: ', total);
			res.pipe(progress).pipe(outputStream);
		})
		.on('error', e => {
			console.warn('FRP - error downloading binary', e);
			cb(e);
		});
	
	// 写入缓存路径中
	const outputStream = fs.createWriteStream(cacheTmpUrl)
		.on('error', e => {
			console.log('FRP - 将二进制文件存储到本地文件时出错', e);
			// 再次尝试安装
			cb(e);
		})
		.on('finish', () => {
			fs.renameSync(cacheTmpUrl, cacheUrl)
			// console.log('FRP - 二进制资源下载到 ' + cacheUrl);
			console.log('\nFRP - 二进制资源下完毕！');
			extract(cb);
		});
}

// 进度流
function progressStream(msg, total) {
	let downloaded = 0;
	let shouldClearLine = false;
	// 日志输出
	const log = () => {
		if (shouldClearLine) {
			readline.clearLine(process.stdout);
			readline.cursorTo(process.stdout, 0);
		}
		// 进度百分比
		// let progress = Math.round(downloaded * 10000 / total) / 100 + '%'; // downloaded + (total ? ('/' + total) : '');
		// 输出
		// process.stdout.write(msg + progress);
		process.stdout.write(msg + logProgressRender(downloaded, total, 50));
		shouldClearLine = true;
	};
	if (total > 0) log();
	return new Transform({
		transform(data, enc, cb) {
			downloaded += data.length;
			log();
			cb(null, data);
		}
	});
}


// 提取
function extract(cb) {
	console.log('FRP - 开始解压二进制压缩包');
	// 当前项目bin目录
	const moduleBinPath = binDir;
	// 资源后缀
	const suffix = os.platform() === 'win32' ? '.exe' : '';
	// 解压 (使用说明：https://github.com/kevva/decompress)
	decompress(cacheUrl, moduleBinPath, {
		// 删除前面的目录
		strip: 1,
		filter: file => ['frpc' + suffix, 'frps' + suffix].indexOf(file.path) !== -1
	})
		.then(() => {
			// 在windows 下资源复制
			if (suffix === '.exe') {
			
			}
			// 拼装工具完整路径
			const clientTarget = path.join(moduleBinPath, 'frpc' + suffix);
			const serverTarget = path.join(moduleBinPath, 'frps' + suffix);
			
			// 修改资源权限
			fs.chmodSync(clientTarget, 755);
			fs.chmodSync(serverTarget, 755);
			// 检查资源是否存在并是否有效
			if (!fs.existsSync(clientTarget) || fs.statSync(clientTarget).size <= 0) {
				return error(new Error('文件损坏 ' + clientTarget));
			}
			if (!fs.existsSync(serverTarget) || fs.statSync(serverTarget).size <= 0) {
				return error(new Error('文件损坏 ' + serverTarget));
			}
			// 
			console.log('FRP - 客户端二进制解压缩为 ' + clientTarget);
			console.log('FRP - 服务端二进制解压缩为 ' + serverTarget);
			cb(null);
		}, e => {
			console.warn('FRP - 解压二进制资源错误', e);
			cb(e);
		});
}

// 尝试安装次数
let attempts = 0;

// 再次尝试安装
function retry(err) {
	attempts++;
	if (err && attempts === maxAttempts) {
		console.error('FRP - 安装错误', err);
		return process.exit(1);
	}
	if (err) {
		console.warn('FRP - 安装失败，重试');
		// 500毫秒后重试
		return setTimeout(download, 500, retry);
	}
	process.exit(0);
}

// 进度日志渲染
function logProgressRender(completed, total, barlen) {
	const barLength = Number(barlen) || 25
	const percent = (completed / total).toFixed(4);     // 计算进度(子任务的 完成数 除以 总数)
	const cell_num = Math.floor(percent * barLength);     // 计算需要多少个 █ 符号来拼凑图案
	
	// 拼接黑色条
	let cell = '';
	for (let i = 0; i < cell_num; i++) {
		// cell += '█';
		cell += '=';
	}
	
	// 拼接灰色条
	let empty = '';
	for (let i = 0; i < barLength - cell_num; i++) {
		// empty += '░';
		empty += ' ';
	}
	// 拼接最终文本
	return (100 * percent).toFixed(2) + '% [' + cell + empty + '] ' + completed + '/' + total;
};


